#include "U2GLESPixelUtil.h"

#include "U2Bitwise.h"
#include "U2RenderSystemCapabilities.h"
#include "U2Root.h"



U2EG_NAMESPACE_USING


GLenum U2GLESPixelUtil::getGLOriginFormat(PixelFormat mFormat)
{
    switch (mFormat)
    {
        case PF_A8:
            return GL_ALPHA;

        case PF_L8:
        case PF_L16:
        case PF_FLOAT16_R:
        case PF_FLOAT32_R:
            return GL_LUMINANCE;

        case PF_BYTE_LA:
        case PF_SHORT_GR:
        case PF_FLOAT16_GR:
        case PF_FLOAT32_GR:
            return GL_LUMINANCE_ALPHA;

        // PVRTC compressed formats
#if GL_IMG_texture_compression_pvrtc
        case PF_PVRTC_RGB2:
            return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
        case PF_PVRTC_RGB4:
            return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
        case PF_PVRTC_RGBA2:
            return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
        case PF_PVRTC_RGBA4:
            return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
#endif
        case PF_R5G6B5:
        case PF_B5G6R5:
            return GL_RGB;

//         case PF_A1R5G5B5:
//             return GL_BGRA;
        case PF_A4R4G4B4:
        case PF_X8R8G8B8:
        case PF_A8R8G8B8:
        case PF_B8G8R8A8:
        case PF_X8B8G8R8:
        case PF_A8B8G8R8:
            return GL_RGBA;

#if U2_ENDIAN == U2_ENDIAN_BIG
        // Formats are in native endian, so R8G8B8 on little endian is
        // BGR, on big endian it is RGB.
        case PF_R8G8B8:
            return GL_RGB;
        case PF_B8G8R8:
            return 0;
#else
        case PF_R8G8B8:
            return 0;
        case PF_B8G8R8:
            return GL_RGB;
#endif
        case PF_A1R5G5B5:
        case PF_DXT1:
        case PF_DXT3:
        case PF_DXT5:
        case PF_R3G3B2:
        case PF_A2R10G10B10:
        case PF_A2B10G10R10:
        case PF_SHORT_RGB:
        case PF_FLOAT16_RGB:
        case PF_FLOAT32_RGB:
        case PF_FLOAT16_RGBA:
        case PF_FLOAT32_RGBA:
        case PF_SHORT_RGBA:
        default:
            return 0;
    }
}

GLenum U2GLESPixelUtil::getGLOriginDataType(PixelFormat mFormat)
{
    switch (mFormat)
    {
        case PF_A8:
        case PF_L8:
        case PF_L16:
        case PF_R8G8B8:
        case PF_B8G8R8:
        case PF_BYTE_LA:
            return GL_UNSIGNED_BYTE;
        case PF_R5G6B5:
        case PF_B5G6R5:
            return GL_UNSIGNED_SHORT_5_6_5;
        case PF_A4R4G4B4:
			return GL_UNSIGNED_SHORT_4_4_4_4;
        case PF_A1R5G5B5:
            return GL_UNSIGNED_SHORT_5_5_5_1;

#if U2_ENDIAN == U2_ENDIAN_BIG
        case PF_X8B8G8R8:
        case PF_A8B8G8R8:
            return GL_UNSIGNED_INT_8_8_8_8_REV;
        case PF_X8R8G8B8:
        case PF_A8R8G8B8:
            return GL_UNSIGNED_INT_8_8_8_8_REV;
        case PF_B8G8R8A8:
            return GL_UNSIGNED_BYTE;
        case PF_R8G8B8A8:
            return GL_UNSIGNED_BYTE;
#else
        case PF_X8B8G8R8:
        case PF_A8B8G8R8:
        case PF_X8R8G8B8:
        case PF_A8R8G8B8:
        case PF_B8G8R8A8:
        case PF_R8G8B8A8:
            return GL_UNSIGNED_BYTE;
#endif
        case PF_DXT1:
        case PF_DXT3:
        case PF_DXT5:
        case PF_R3G3B2:
        case PF_A2R10G10B10:
        case PF_A2B10G10R10:
        case PF_FLOAT16_RGB:
        case PF_FLOAT16_RGBA:
        case PF_FLOAT32_R:
        case PF_FLOAT32_GR:
        case PF_FLOAT32_RGB:
        case PF_FLOAT32_RGBA:
        case PF_SHORT_RGBA:
        case PF_SHORT_RGB:
        case PF_SHORT_GR:
            // TODO not supported
        default:
            return 0;
    }
}

GLenum U2GLESPixelUtil::getGLInternalFormat(PixelFormat fmt, bool hwGamma)
{
    switch (fmt)
    {
        case PF_L8:
        case PF_L16:
            return GL_LUMINANCE;

        case PF_A8:
            return GL_ALPHA;

        case PF_BYTE_LA:
            return GL_LUMINANCE_ALPHA;

#if GL_IMG_texture_compression_pvrtc
        case PF_PVRTC_RGB2:
            return GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG;
        case PF_PVRTC_RGB4:
            return GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG;
        case PF_PVRTC_RGBA2:
            return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;
        case PF_PVRTC_RGBA4:
            return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;
#endif
            
        case PF_X8B8G8R8:
        case PF_X8R8G8B8:
        case PF_A8R8G8B8:
        case PF_B8G8R8A8:
        case PF_A1R5G5B5:
        case PF_A4R4G4B4:
            return GL_RGBA;
        case PF_R5G6B5:
        case PF_B5G6R5:
        case PF_R8G8B8:
        case PF_B8G8R8:
            return GL_RGB;
        case PF_A4L4:
        case PF_R3G3B2:
        case PF_A2R10G10B10:
        case PF_A2B10G10R10:
        case PF_DXT1:
        case PF_DXT3:
        case PF_DXT5:
        case PF_FLOAT16_R:
        case PF_FLOAT16_RGB:
        case PF_FLOAT16_GR:
        case PF_FLOAT16_RGBA:
        case PF_FLOAT32_R:
        case PF_FLOAT32_GR:
        case PF_FLOAT32_RGB:
        case PF_FLOAT32_RGBA:
        case PF_SHORT_RGBA:
        case PF_SHORT_RGB:
        case PF_SHORT_GR:
        default:
            return 0;
    }
}

GLenum U2GLESPixelUtil::getClosestGLInternalFormat(PixelFormat mFormat,
                                               bool hwGamma)
{
    GLenum format = getGLInternalFormat(mFormat, hwGamma);
    if (format == 0)
    {
        if (hwGamma)
        {
            // TODO not supported
            return 0;
        }
        else
        {
            return GL_RGBA;
        }
    }
    else
    {
        return format;
    }
}

PixelFormat U2GLESPixelUtil::getClosestOGREFormat(GLenum fmt, GLenum dataType)
{
    switch (fmt)
    {
#if GL_IMG_texture_compression_pvrtc
        case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:
            return PF_PVRTC_RGB2;
        case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:
            return PF_PVRTC_RGBA2;
        case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:
            return PF_PVRTC_RGB4;
        case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:
            return PF_PVRTC_RGBA4;
#endif
        case GL_LUMINANCE:
            return PF_L8;
        case GL_ALPHA:
            return PF_A8;
        case GL_LUMINANCE_ALPHA:
            return PF_BYTE_LA;
        
        case GL_RGB:
            switch(dataType)
            {
                case GL_UNSIGNED_SHORT_5_6_5:
                    return PF_B5G6R5;
                default:
                    return PF_R8G8B8;
            };
        case GL_RGBA:
            switch(dataType)
            {
                case GL_UNSIGNED_SHORT_5_5_5_1:
                    return PF_A1R5G5B5;
                case GL_UNSIGNED_SHORT_4_4_4_4:
                    return PF_A4R4G4B4;
                default:
#if (U2_PLATFORM == U2_PLATFORM_APPLE_IOS)
                    // seems that in iPhone we need this value to get the right color
                    return PF_A8R8G8B8;
#else
                    return PF_X8B8G8R8;
#endif
            }
#ifdef GL_BGRA
        case GL_BGRA:
            return PF_A8B8G8R8;
#endif

        default:
            //TODO: not supported
            return PF_A8R8G8B8;
    };
}

size_t U2GLESPixelUtil::getMaxMipmaps(size_t width, size_t height, size_t depth,
                                  PixelFormat format)
{
    size_t count = 0;

    do {
        if (width > 1)
        {
            width = width / 2;
        }
        if (height > 1)
        {
            height = height / 2;
        }
        if (depth > 1)
        {
            depth = depth / 2;
        }
        /*
        NOT needed, compressed formats will have mipmaps up to 1x1
        if(PixelUtil::isValidExtent(width, height, depth, format))
            count ++;
        else
            break;
        */
        count++;
    } while (!(width == 1 && height == 1 && depth == 1));

    return count;
}

size_t U2GLESPixelUtil::optionalPO2(size_t value)
{
    /*****************@@@@@@@@@@@@@@@@@@@@@
    const U2RenderSystemCapabilities *caps =
        U2Root::getSingleton().getRenderSystem()->getCapabilities();

    if (caps->hasCapability(RSC_NON_POWER_OF_2_TEXTURES))
    {
        return value;
    }
    else
    ******************************/
    {
        return U2Bitwise::firstPO2From((u2uint32)value);
    }
}

void U2GLESPixelUtil::convertToGLformat(const U2PixelBox &src, const U2PixelBox &dst)
{
    // Always need to convert PF_A4R4G4B4, GL expects the colors to be in the 
    // reverse order
    if (dst.format == PF_A4R4G4B4)
    {
        // Convert PF_A4R4G4B4 -> PF_B4G4R4A4
        // Reverse pixel order
        u2uint16 *srcptr = static_cast<u2uint16*>(src.data)
		+ (src.left + src.top * src.rowPitch + src.front * src.slicePitch);
        u2uint16 *dstptr = static_cast<u2uint16*>(dst.data)
		+ (dst.left + dst.top * dst.rowPitch + dst.front * dst.slicePitch);
        const size_t srcSliceSkip = src.getSliceSkip();
        const size_t dstSliceSkip = dst.getSliceSkip();
        const size_t k = src.right - src.left;
        for(size_t z=src.front; z<src.back; z++) 
        {
            for(size_t y=src.top; y<src.bottom; y++)
            {
                for(size_t x=0; x<k; x++)
                {
                    dstptr[x] = ((srcptr[x]&0x000F)<<12) |  // B
                                ((srcptr[x]&0x00F0)<<4) |   // G
                                ((srcptr[x]&0x0F00)>>4) |   // R
                                ((srcptr[x]&0xF000)>>12);   // A
                }
                srcptr += src.rowPitch;
                dstptr += dst.rowPitch;
            }
            srcptr += srcSliceSkip;
            dstptr += dstSliceSkip;
        }    
    }
}